Code
library(tidyverse)
library(tidymodels)
library(corrplot)
library(explore)
library(ggplot2)
library(corrplot)
library(dplyr)
library(viridis)Im folgenden Teil dieser Arbeit werden die Vorbereitungen getroffen, die notwendig sind um die Durchführung des Projekts zu ermöglichen.
https://www.kaggle.com/datasets/amirmahdiabbootalebi/salary-by-job-title-and-country
Zusätzliche Quellen für die Methodik:
https://www.datanovia.com/en/blog/top-r-color-palettes-to-know-for-great-data-visualization/
https://ggplot2.tidyverse.org/reference/
ggplot2 - Elegante R Plots (statistikprofis.com)
Dieses Datenset bietet eine umfassende Sammlung von Gehaltsinformationen aus verschiedenen Branchen und Regionen weltweit. Es enthält Details zu Berufsbezeichnungen, Gehältern, Berufssektoren, geografischen Standorten und mehr, die von seriösen Beschäftigungswebsites und Umfragen stammen. Analysieren Sie diese Daten, um Einblicke in Trends auf dem Arbeitsmarkt zu gewinnen, Vergütungen in verschiedenen Berufen zu vergleichen und informierte Entscheidungen über Ihre Karriere oder Einstellungsstrategien zu treffen. Das Datenset ist zur einfachen Analyse bereinigt und vorverarbeitet und steht unter einer offenen Lizenz für Forschungs- und Datenanalysezwecke zur Verfügung.
library(tidyverse)
library(tidymodels)
library(corrplot)
library(explore)
library(ggplot2)
library(corrplot)
library(dplyr)
library(viridis)Häufig kommt:
WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding: https://cran.rstudio.com/bin/windows/Rtools/ Warning in install.packages : Paket ‘dplyr’ wird gerade benutzt und deshab nicht installiert
Installiere RTools nach Link: https://cran.rstudio.com/bin/windows/Rtools/rtools43/rtools.html
Der Datensatz, der in diesem Projekt analysiert wird, stammt von der website “Kaggle” und beschreibt das Gehalt nach Job und Land in dem gearbeitet wird.
salary <- read_csv("Salary.csv")Rows: 6684 Columns: 9
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (4): Gender, Job Title, Country, Race
dbl (5): Age, Education Level, Years of Experience, Salary, Senior
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Um einen ersten Überblick zu erhalten, werden die ersten 10 Zeilen der Tabelle ausgelesen:
head(salary, 10)# A tibble: 10 × 9
Age Gender `Education Level` `Job Title` `Years of Experience` Salary
<dbl> <chr> <dbl> <chr> <dbl> <dbl>
1 32 Male 1 Software Engineer 5 90000
2 28 Female 2 Data Analyst 3 65000
3 45 Male 3 Manager 15 150000
4 36 Female 1 Sales Associate 7 60000
5 52 Male 2 Director 20 200000
6 29 Male 1 Marketing Analyst 2 55000
7 42 Female 2 Product Manager 12 120000
8 31 Male 1 Sales Manager 4 80000
9 26 Female 1 Marketing Coordi… 1 45000
10 38 Male 3 Scientist 10 110000
# ℹ 3 more variables: Country <chr>, Race <chr>, Senior <dbl>
Mithilfe der “describe_tbl”- Funktion können die generellen Informationen über den Datensatz ermittelt werden.
describe_tbl(salary)6 684 (6.7k) observations with 9 variables
0 observations containing missings (NA)
0 variables containing missings (NA)
0 variables with no variance
Wie oben zu erkennen, enthält der Datensatz 6684 Instanzen, wovon keine einen Wert ohne Angabe (NA’s) besitzt.
Nun wird ein kurzer Blick auf die Art der Merkmale geholfen. Gibt es kategorische oder nummerische Merkmale innerhalb des Datensatzes?
describe(salary)# A tibble: 9 × 8
variable type na na_pct unique min mean max
<chr> <chr> <int> <dbl> <int> <dbl> <dbl> <dbl>
1 Age dbl 0 0 41 21 33.6 62
2 Gender chr 0 0 2 NA NA NA
3 Education Level dbl 0 0 4 0 1.62 3
4 Job Title chr 0 0 129 NA NA NA
5 Years of Experience dbl 0 0 37 0 8.08 34
6 Salary dbl 0 0 437 350 115307. 250000
7 Country chr 0 0 5 NA NA NA
8 Race chr 0 0 10 NA NA NA
9 Senior dbl 0 0 2 0 0.14 1
| Spalte | Typ | Bedeutung |
|---|---|---|
| Age | Numerisch | Alter |
| Gender | Kategorisch | Geschlecht |
| Education Level | Numerisch | Bildungsgrad |
| Job Title | Kategorisch | Jobtitel |
| Years of Experience | Numerisch | Arbeitserfahrung in Jahren |
| Salary | Numerisch | Gehalt |
| Country | Kategorisch | Land |
| Race | Kategorisch | Ethnizität |
| Senior | Numerisch | Senior position ja(1)/nein(0) |
Im folgenden Abschnitt werden verschiedene Funktionen dafür verwendet, um die Datentypen und Bedeutung der Spalten zu verstehen.
salary <- salary |>
rename(
Job.Title = `Job Title`,
Years.Of.Experience = `Years of Experience`,
Education.Level = `Education Level`
)Hier werden die Spaltennamen der Spalten verändert, welche ein Leerzeichen im Namen haben. Es handelt sich hierbei um die Spalten “Job Title”, “Years of Experience” und “Education level”. Das Leerzeichen wird einfach durch einen Punkt ersetzt. Da noch häufig im Laufe des Projektes auf die Spaltennamen zugegriffen werdne muss, wird Uns das in der Zukunft noch Zeit sparen.
Nun werfen verschaffen Wir uns einen Überblick über die prozentuale Verteilung der Jobtitel. Aus der Grafik geht hervor, dass der Beruf des “Data Scientist” der meist ausgeführte Beruf ist. Außerdem gibt es innerhalb des Datensatzes auch viele “Data Analsysten” , sowie auch “Backend Devolper”.
explore (salary, Job.Title)Altersverteilung:
# Erstelle eine Histogramm-Visualisierung der Altersverteilung
ggplot(salary, aes(x = Age)) +
geom_histogram(binwidth = 5, fill = "skyblue", color = "black", alpha = 0.8) +
labs(title = "Age Distribution",
x = "Age",
y = "Frequency")Verteilung des Bildungsniveaus
# Definiere eine Farbpalette mit 4 verschiedenen Farben
my_colors <- c("skyblue", "lightgreen", "salmon", "gold") # Farben nach Wunsch ändern
# Erstelle ein Balkendiagramm mit unterschiedlichen Farben für jede Stange basierend auf dem Bildungsniveau
ggplot(salary, aes(x = factor(`Education.Level`))) +
geom_bar(fill = my_colors, color = "black") +
labs(title = "Verteilung des Bildungsniveaus",
x = "Bildungsniveau",
y = "Anzahl") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))Verteilung der Arbeitserfahrung in Jahren
Ab hier wurde Teilweise das Paket “Viridis” für die farbliche Darstellung verwendet um eine Alternative zur Manuellen Deklaration der Farben aufzuzeigen.
# Erstelle ein Histogramm für die Verteilung der Jahre an Erfahrung
ggplot(salary, aes(x = `Years.Of.Experience`)) +
geom_histogram(binwidth = 5, fill = viridis(8), color = "black") +
labs(title = "Verteilung der Berufserfahrung",
x = "Jahre an Erfahrung",
y = "Häufigkeit") +
theme_minimal()Verteilung der Geschlechter
# Erstellen des Diagramms
ggplot(salary, aes(x=Gender)) +
geom_bar(fill=viridis(2)) +
ggtitle("Gender Distribution") +
xlab("Gender") +
ylab("Count") +
theme(plot.title = element_text(hjust = 0.5))Verteilung der Länder
(Alternative Nutzung des Farbschemas)
# Erstellen des Diagramms
ggplot(salary, aes(x = Country, fill = Country)) +
geom_bar() +
scale_fill_viridis(discrete = TRUE) +
ggtitle("Country Distribution") +
xlab("Country") +
ylab("Count") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))Verteilung der Ethnizitäten
# Erstellen des Diagramms
ggplot(salary, aes(x = Race, fill = Race)) +
geom_bar() +
scale_fill_viridis(discrete = TRUE) +
ggtitle("Race Distribution") +
xlab("Race") +
ylab("Count") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))Verteilung der 10 Häufigsten Job Titel
# Die Top 10 Jobtitel auswählen
top_job_titles <- names(sort(table(salary$Job.Title), decreasing = TRUE)[1:10])
# Zufällige Farben für jeden Jobtitel generieren
job_colors <- rainbow(length(top_job_titles))
# Daten filtern und ggplot erstellen
ggplot(salary[salary$Job.Title %in% top_job_titles, ], aes(x = factor(Job.Title, levels = top_job_titles), fill = factor(Job.Title))) +
geom_bar(fill=viridis(10)) +
scale_fill_manual(values = job_colors) +
labs(title = "Top 10 Job Titles Distribution",
x = "Job Title",
y = "Count") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))Zunächst wird mithilfe der Funktion “summary( )” ein allgemeiner Überblick über die wichtigsten charackteristeischen Merkmale der einzelnen Splaten gegeben.
summary(salary) Age Gender Education.Level Job.Title
Min. :21.00 Length:6684 Min. :0.000 Length:6684
1st Qu.:28.00 Class :character 1st Qu.:1.000 Class :character
Median :32.00 Mode :character Median :1.000 Mode :character
Mean :33.61 Mean :1.622
3rd Qu.:38.00 3rd Qu.:2.000
Max. :62.00 Max. :3.000
Years.Of.Experience Salary Country Race
Min. : 0.000 Min. : 350 Length:6684 Length:6684
1st Qu.: 3.000 1st Qu.: 70000 Class :character Class :character
Median : 7.000 Median :115000 Mode :character Mode :character
Mean : 8.078 Mean :115307
3rd Qu.:12.000 3rd Qu.:160000
Max. :34.000 Max. :250000
Senior
Min. :0.0000
1st Qu.:0.0000
Median :0.0000
Mean :0.1435
3rd Qu.:0.0000
Max. :1.0000
Hier alles genauer beschreiben
Erkennbar hier ist es, dass es innerhalb des Datensatzes ein durchschnittliches Alter von 32 Jahren vorliegt. Das Alter streckt sich von 21 Jahren bis zu 62 Jahren. Außerdem gibt es beim “Education-Level” Werte zwischen 1, 2 und 3, wobei der Durchschnitt jedoch bei 1 liegt. Außerdem gibt es bei der Berufserfahrung ( Years of Experience) Werte zwischen 0 bis zu 34 Jahren. Der Median hier beträgt 7.
Im folgenden wird der Datensatz temporär umstrukturiert um den Datensatz besser analysieren und visualieren zu können.
Ein neuer Wert Namens “Value” wird erschaffen.
Salary_long <- select(salary, -Job.Title, -Gender, -Race, -Country, -Senior)
Salary_long <- pivot_longer(Salary_long, colnames(Salary_long))
Salary <- as.data.frame(Salary_long)
head(Salary_long)# A tibble: 6 × 2
name value
<chr> <dbl>
1 Age 32
2 Education.Level 1
3 Years.Of.Experience 5
4 Salary 90000
5 Age 28
6 Education.Level 2
Insgesamt werden in diesem Codechunk die Spalten die nicht nummerische Merkmale sind entfernt und der verbleibende Datensatz wird von einem breiten in ein längeres Format umgewandelt.
Hier kann man folgende Dinge erkennen:
Age, Years of Experience und Education Level sind Linksschief und haben ggf. Bedarf einer Transformation für ML-Modelle
Age und Years of Experience haben Extrempunkte im oberen Wertebereich, während Salary einer gleichmäßigen Verteilung folgt
Aufgrund der guten Strukturierung der Daten ,eignen sie sich dem ersten Anschein nach gut für eine Ausführliche Explorative Analyse.
Zunächst werden die Daten aus dem Ausgangsdatensatz in einen finalen Datensatz “salary_final” geschrieben.
salary_final <- salaryDurch den Befehl “hist()” wird ein Histogramm erstellt . Es ermöglicht eine visuelle Darstellung der Häufigkeitsverteilung dieses GEhlatsdaten, indem es zeigt, wie oft bestimmte Gehaltsbereiche vorkommen.
Verteilung des Gehalts
# Erstelle ein Histogramm für die Gehaltsverteilung
ggplot(salary, aes(x = Salary)) +
geom_histogram(binwidth = 10000, fill = viridis(26), color = "black") +
labs(title = "Gehaltsverteilung",
x = "Gehalt",
y = "Häufigkeit") +
scale_y_continuous(labels = scales::comma) +
scale_x_continuous(labels = scales::comma) +
theme_minimal()Im folgenden wird eine neue Spalte “SalaryKat” erstellt die kategorische Werte basdierend auf den Gehältern enthält…
salary_final$SalaryKat <- cut(salary_final$Salary,
breaks = c(-Inf, 50000, 100000, 150000, 200000, 250000, Inf), labels = c("50000", "100000", "150000", "200000","250000", "300000"))# Erstellen des Balkendiagramms für die 5 Gategorien des Gehalts
ggplot(salary_final, aes(x = SalaryKat, fill = SalaryKat)) +
geom_bar() +
scale_fill_viridis(discrete = TRUE) +
ggtitle("Verteilung der Gehaltskategorien") +
xlab("Gehaltskategorie") +
ylab("Anzahl") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))Im Folgenden werden die Korrelationen zwischen den Verschiedenen Spalten errechnet.
Die Berechnung von Korrelationen ermöglicht es, die Stärke und Richtung des Zusammenhangs zwischen zwei Variablen zu quantifizieren. Dies hilft bei der Modellvalisierung, um potenzielle Probleme, wie zum Beispiel die Multikollinearität zu erkennen.
Nun werden verschiedene Korrelationen errechnet:
# Korrelation zwischen Salary und Years.Of.Experience berechnen
correlation_salary_experience <- cor(salary_final$Salary, salary_final$Years.Of.Experience)
# Ausgabe des Ergebnisses
cat("Die Korrelation zwischen Salary und Years.Of.Experience ist:", correlation_salary_experience, "\n")Die Korrelation zwischen Salary und Years.Of.Experience ist: 0.8109416
“Hier würde Ich keinen Text vorschreiben, Ausgabe aussagekräftig genug”
# Korrelation zwischen Salary und Age berechnen
correlation_salary_age <- cor(salary_final$Salary, salary_final$Age, use = "complete.obs")
# Ausgabe des Ergebnisses
cat("Die Korrelation zwischen Salary und Age ist:", correlation_salary_age, "\n")Die Korrelation zwischen Salary und Age ist: 0.7283429
“Hier würde Ich keinen Text vorschreiben, Ausgabe aussagekräftig genug”
# Korrelation zwischen Years.Of.Experience und Age berechnen
correlation_experience_age <- cor(salary_final$Years.Of.Experience, salary_final$Age, use = "complete.obs")
# Ausgabe des Ergebnisses
cat("Die Korrelation zwischen Years.Of.Experience und Age ist:", correlation_experience_age, "\n")Die Korrelation zwischen Years.Of.Experience und Age ist: 0.9376094
“Hier würde Ich keinen Text vorschreiben, Ausgabe aussagekräftig genug”
# Korrelation zwischen Seniority und Years.Of.Experience berechnen
correlation_seniority_experience <- cor(salary_final$Senior, salary_final$Years.Of.Experience, use = "complete.obs")
# Ausgabe des Ergebnisses
cat("Die Korrelation zwischen Seniority und Years.Of.Experience ist:", correlation_seniority_experience, "\n")Die Korrelation zwischen Seniority und Years.Of.Experience ist: 0.3178772
das ergebnis der correlationen: von salary und years.of.experience ist es 0.81 von Salary und Age ist es 0.73 und die von Age und Years.Of.Experience ist 0.93 wie kommt so ein starker unterschied zu stande bei den werten im vergleich zu salary obwohl sie doch eine hohe correlation zueinander haben
Verteilung der Daten: Es ist möglich, dass die Verteilung der Daten in den Variablen “Age” und “Years.Of.Experience” anders ist als in der Variable “Salary”. Wenn die Daten in “Age” und “Years.Of.Experience” breiter gestreut sind, kann dies zu einer geringeren Korrelation führen, selbst wenn eine starke lineare Beziehung besteht.
Nicht-lineare Beziehung: Die Korrelation misst nur lineare Beziehungen. Wenn die Beziehung zwischen “Age” und “Years.Of.Experience” nicht linear ist, könnte dies zu einem niedrigeren Korrelationswert führen.
Ausreißer: Das Vorhandensein von Ausreißern kann die Korrelation beeinflussen. Wenn es Ausreißer in einer der Variablen gibt, kann dies den Korrelationswert beeinträchtigen.
Stichprobengröße: Bei kleineren Stichproben können Korrelationswerte instabiler sein.
Im folgenden werden anhand der Daten ein paar Tests durchgeführt um Aussagen für die Thesen heruaszufiltern. Dies geschieht mithilfe einer Visualisierung der Beziehungen zwischen den verschiedenen Spalten, sowie mitihilfe von Korrelationen.
Das Ergebnis dieses Codechunks ist eine Darstellung der Korrelationsmatrix:
correlations <- cor(salary_final[, c("Age", "Education.Level", "Years.Of.Experience", "Salary")])
print(correlations) Age Education.Level Years.Of.Experience Salary
Age 1.0000000 0.5963804 0.9376094 0.7283429
Education.Level 0.5963804 1.0000000 0.6131650 0.6454436
Years.Of.Experience 0.9376094 0.6131650 1.0000000 0.8109416
Salary 0.7283429 0.6454436 0.8109416 1.0000000
Erkennbar hier ist eine starke Korrelation zwischen dem Alter und den “Years of Experience”. Desweiteren liegt auch eine starke Korrelation zwischnem den Years of Experience und dem entgültigen Gehalt. Eine nicht so starke Korrelation liegt zwischen dem Alter und dem Education Level mit einem Wert von ungefähr 0,6.
filtered_data_numeric <- select(salary, Salary, Age, Years.Of.Experience, Education.Level)
glimpse(filtered_data_numeric)Rows: 6,684
Columns: 4
$ Salary <dbl> 90000, 65000, 150000, 60000, 200000, 55000, 120000…
$ Age <dbl> 32, 28, 45, 36, 52, 29, 42, 31, 26, 38, 29, 48, 35…
$ Years.Of.Experience <dbl> 5, 3, 15, 7, 20, 2, 12, 4, 1, 10, 3, 18, 6, 14, 2,…
$ Education.Level <dbl> 1, 2, 3, 1, 2, 1, 2, 1, 1, 3, 2, 1, 1, 2, 1, 1, 2,…
cor(filtered_data_numeric) Salary Age Years.Of.Experience Education.Level
Salary 1.0000000 0.7283429 0.8109416 0.6454436
Age 0.7283429 1.0000000 0.9376094 0.5963804
Years.Of.Experience 0.8109416 0.9376094 1.0000000 0.6131650
Education.Level 0.6454436 0.5963804 0.6131650 1.0000000
#Erstellen des Korrelationplots
corrplot(cor(filtered_data_numeric), method = "ellipse", col = viridis(200))#Erstellen des Streudiagrammes
ggplot(salary_final, aes(x = Years.Of.Experience, y = Salary)) +
geom_point(color = viridis(2)[1], size = 3, shape = 16) +
labs(title = "Streudiagramm von Berufserfahrung vs. Gehalt",
x = "Berufserfahrung",
y = "Gehalt")In diesem Streudiagramm ist erkennbar, das es einen eindeutigen Trend nach oben gibt je mehr “Years of Experience” vorliegen. So ist auch zu sehen, dass die Topgehälter von 250.000€ zwischen 20 bis 30 Erfahrungsjahren liegen.
ggplot(salary_final, aes(x = Education.Level, y = Salary)) +
geom_point(color = viridis(2)[1], size = 3, shape = 16) +
labs(title = "Scatter Plot of Education Level vs Salary",
x = "Years of Experience",
y = "Salary")Die vorliegende Datenanalyse zeigt einen klaren Trend zu höheren Gehaltsklassen, der mit einem Anstieg des Bildungsniveaus einhergeht. Diese Tendenz wird durch eine höhere Dichte in den oberen Gehaltsgruppen für Personen mit dem dritten Bildungsgrad im Vergleich zum zweiten und ersten Bildungsgrad deutlich.
In diesem Abschnitt werden Balkendiagramme verwendet um den Datensatz auf Beziehungen zu analysieren.
#Erstellen des Diagramms
ggplot(salary_final, aes(x = Race, y = Salary, fill = Race)) +
stat_summary(fun = "mean", geom = "bar") +
scale_fill_viridis(discrete = TRUE) +
ggtitle("Durchschnittliches Gehalt nach Ethnizität") +
xlab("Rasse") +
ylab("Durchschnittliches Gehalt")Die Grafik macht deutlich, dass die Gruppen “Black, Korean, Mixed und White” im Durchschnitt am meisten verdienen.
ggplot(salary_final, aes(x = Country, y = Salary, fill = Country)) +
stat_summary(fun = "mean", geom = "bar", position = "dodge", color = "black") +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Durchschnittliches Gehalt nach Land",
x = "Land",
y = "Durchschnittliches Gehalt") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))Es ist ersichtlich, dass in den Ländern “Canada und China” das durchschnittliche Gehalt am größten ist. Jedochg ist zu erwähnen, dass alle Länder nah bei einander liegen.
In diesem Fall wird ein gestapeltes Balkendiagramm erstellt. Die Balken sind nach Geschlecht gruppiert und gestapelt. DIes ermöglicht einen Vergleich der durchschnittlichen Gehälter zwischen den Ländern und Geschlechtern
ggplot(salary_final, aes(x = Country, y = Salary, fill = Gender)) +
geom_bar(stat = "summary", fun = "mean", position = "stack", color = "black") +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Average Salary by Country and Gender",
x = "Country",
y = "Average Salary") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) Hier ist zu erkennen, das alle Länder ungefähr die gleiche Verteilung zwischen “Male” und “Female” haben.
Zur Visualisierung der durchnittlichen Bezahlung für Länder und Bildungsniveau wird ein gruppiertes Balkendiagramm verwendet. DIe Balken sind nach Bildungsniveau und nebeneinander gruppiert.
Desweiteren sind zur besseren Veranschaulichung die Beschriftungen auf der X-Achse um 45 Grad gedreht.
ggplot(salary_final, aes(x = Country, y = Salary, fill = factor(Education.Level))) +
geom_bar(stat = "summary", fun = "mean", position = "dodge", color = "black") +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Average Salary by Country and Education Level",
x = "Country",
y = "Average Salary") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) Es ist deutlich zu erkennen, dass die Gehälter in jedem Land deutlich ansteigen je höher das Bildungsniveau ist.
Auch hier wird zum Vergleich der durchschnittlichen Gehälter zwischen den Ländern und ethnischen Gruppen, ein gestapeltes Balkendiagramm erstellt. Die Balken sind nach ethnischer Gruppe gruppiert und gestapelt.
ggplot(salary_final, aes(x = Country, y = Salary, fill = Race)) +
geom_bar(stat = "summary", fun = "mean", position = "stack", color = "black") +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Average Salary by Country and Race",
x = "Country",
y = "Average Salary") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) !!!!! Salary passt nicht!!!!!!!
Hier ist deutlich sichtbar, dass nicht in jedem Land logischerweise jede ethnische Gruppe vertreten ist. So sind nur in 2 Ländern mehr als 3 verschiedene Gruppen in diesem Datensatz aufgeführt
ggplot(salary_final, aes(x = Job.Title, y = Salary)) +
geom_bar(stat = "summary", fun = "mean", color = viridis(2)[1], color = viridis(2)[1]) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Average Salary by Job Title",
x = "Job Title",
y = "Average Salary") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) Warning: Duplicated aesthetics after name standardisation: colour
Hier wird festgestellt das in dem Datensatz zu viele Jobtitle vorkommen
job_title_count <- table(salary_final$Job.Title)
print(job_title_count)
Account Executive Account Manager
1 4
Accountant Administrative Assistant
6 2
Advertising Coordinator Back end Developer
1 242
Business Analyst Business Development Associate
20 7
Business Development Manager Business Intelligence Analyst
5 1
Business Operations Analyst CEO
2 1
Chief Data Officer Chief Technology Officer
1 1
Consultant Content Marketing Manager
1 73
Copywriter Creative Director
2 1
Customer Service Manager Customer Service Rep
2 1
Customer Service Representative Customer Success Manager
6 1
Customer Success Rep Customer Support Specialist
1 1
Data Analyst Data Engineer
391 4
Data Entry Clerk Data Scientist
1 515
Delivery Driver Designer
5 1
Developer Digital Content Producer
1 1
Digital Marketing Manager Digital Marketing Specialist
52 15
Director Director of Business Development
1 1
Director of Data Science Director of Engineering
57 2
Director of Finance Director of HR
2 69
Director of Human Capital Director of Human Resources
1 2
Director of Marketing Director of Operations
88 11
Director of Product Management Director of Sales
1 1
Director of Sales and Marketing Engineer
1 2
Event Coordinator Financial Advisor
2 5
Financial Analyst Financial Manager
53 139
Front end Developer Front End Developer
239 31
Full Stack Engineer Graphic Designer
304 23
Help Desk Analyst HR Coordinator
1 29
HR Generalist HR Manager
104 5
HR Specialist Human Resources Coordinator
1 50
Human Resources Director Human Resources Manager
1 152
Human Resources Specialist IT Consultant
1 2
IT Manager IT Project Manager
1 1
IT Support IT Support Specialist
1 2
Juniour HR Coordinator Juniour HR Generalist
3 3
Manager Marketing Analyst
2 144
Marketing Coordinator Marketing Director
167 65
Marketing Manager Marketing Specialist
315 10
Network Engineer Office Manager
1 1
Operations Analyst Operations Coordinator
8 5
Operations Director Operations Manager
1 122
Principal Engineer Principal Scientist
1 1
Product Designer Product Development Manager
80 1
Product Manager Product Marketing Manager
323 70
Project Coordinator Project Engineer
5 317
Project Manager Public Relations Manager
34 1
Quality Assurance Analyst Receptionist
1 57
Recruiter Research Director
3 75
Research Scientist Researcher
119 1
Sales Associate Sales Director
212 62
Sales Executive Sales Manager
38 58
Sales Operations Manager Sales Representative
1 81
Scientist Social Media Man
3 1
Social Media Manager Social Media Specialist
15 2
Software Architect Software Developer
1 186
Software Engineer Software Engineer Manager
809 376
Software Manager Software Project Manager
1 1
Strategy Consultant Supply Chain Analyst
1 1
Supply Chain Manager Technical Recruiter
1 1
Technical Support Specialist Technical Writer
1 1
Training Specialist UX Designer
2 5
UX Researcher VP of Finance
1 1
VP of Operations Web Designer
1 1
Web Developer
129
Hier nochmal das obere genauer grafisch herausgearbeitet
job_title_count <- table(salary_final$Job.Title)
job_title_df <- data.frame(Job_Title = names(job_title_count), Frequency = as.numeric(job_title_count))
ggplot(job_title_df, aes(x = Job_Title, y = Frequency)) +
geom_bar(stat = "identity", fill = viridis(2)[1], color = "black") +
labs(title = "Frequency of Unique Job Titles",
x = "Job Titles",
y = "Frequency") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) Jeder Job Dargestellt. Es ist zu notieren das es nicht möglich ist die Vielzahl der unterschiedlichen Jobs darzustellen.
library(dplyr)
# Zählen der Vorkommen für jeden Jobtitel
job_title_count <- salary %>%
count(`Job.Title`, sort = TRUE)
job_title_count# A tibble: 129 × 2
Job.Title n
<chr> <int>
1 Software Engineer 809
2 Data Scientist 515
3 Data Analyst 391
4 Software Engineer Manager 376
5 Product Manager 323
6 Project Engineer 317
7 Marketing Manager 315
8 Full Stack Engineer 304
9 Back end Developer 242
10 Front end Developer 239
# ℹ 119 more rows
ggplot(salary_final, aes(x = Country, y = Salary, fill = Race)) +
geom_boxplot() +
scale_fill_viridis(discrete = TRUE) +
stat_summary(fun = "median", geom = "point", shape = 18, size = 3, color = "red", position = position_dodge(width = 0.75)) +
labs(title = "Salary Distribution by Country and Race",
x = "Country",
y = "Salary") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) (Tests der Thesen gibt uns die Antwort darauf wie die Daten aufbereitet werden müssen)
Da in dem Datensatz teilweise Jobs nur einmalig vertreten sind, kann ein erhebliches Stichproben-Bias verursacht werden. Da das mittlere Einkommen ein wichtiges Merkmal in unserer explorativen Datenanalyse darstellt und mindestens 30 Einträge für eine aussagekräftige Stichprobe nötig sind, haben wir uns dazu entschlossen alle Einträge mit N<30 bei der Anzahl der Jobtitel (N) abzuschneiden.
filtered_data <- salary_final %>%
group_by(Job.Title) %>%
summarise(job_count = n()) %>%
filter(job_count > 30) %>%
inner_join(salary_final, by = "Job.Title")
print(filtered_data)# A tibble: 6,398 × 11
Job.Title job_count Age Gender Education.Level Years.Of.Experience Salary
<chr> <int> <dbl> <chr> <dbl> <dbl> <dbl>
1 Back end D… 242 33 Female 2 5 110000
2 Back end D… 242 32 Male 1 4 95000
3 Back end D… 242 26 Female 2 3 90000
4 Back end D… 242 26 Female 2 2 70000
5 Back end D… 242 24 Female 1 1 60000
6 Back end D… 242 26 Female 2 3 90000
7 Back end D… 242 24 Female 2 1 60000
8 Back end D… 242 34 Male 2 6 125000
9 Back end D… 242 29 Female 1 3 85000
10 Back end D… 242 23 Male 1 1 55000
# ℹ 6,388 more rows
# ℹ 4 more variables: Country <chr>, Race <chr>, Senior <dbl>, SalaryKat <fct>
job_title_count <- table(filtered_data$Job.Title)
print(job_title_count)
Back end Developer Content Marketing Manager
242 73
Data Analyst Data Scientist
391 515
Digital Marketing Manager Director of Data Science
52 57
Director of HR Director of Marketing
69 88
Financial Analyst Financial Manager
53 139
Front end Developer Front End Developer
239 31
Full Stack Engineer HR Generalist
304 104
Human Resources Coordinator Human Resources Manager
50 152
Marketing Analyst Marketing Coordinator
144 167
Marketing Director Marketing Manager
65 315
Operations Manager Product Designer
122 80
Product Manager Product Marketing Manager
323 70
Project Engineer Project Manager
317 34
Receptionist Research Director
57 75
Research Scientist Sales Associate
119 212
Sales Director Sales Executive
62 38
Sales Manager Sales Representative
58 81
Software Developer Software Engineer
186 809
Software Engineer Manager Web Developer
376 129
job_title_count <- table(filtered_data$Job.Title)
job_title_df <- data.frame(Job_Title = names(job_title_count), Frequency = as.numeric(job_title_count))
ggplot(job_title_df, aes(x = Job_Title, y = Frequency)) +
geom_bar(stat = "identity", fill = viridis(2)[1], color = "black") +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Frequency of Unique Job Titles",
x = "Job Titles",
y = "Frequency") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))Auf min 30 Jobhäufigkeiten angepasst.
job_title_count_filtered <- table(filtered_data$Job.Title)
cat(paste(names(job_title_count_filtered), ":", job_title_count_filtered, "\n"))Back end Developer : 242
Content Marketing Manager : 73
Data Analyst : 391
Data Scientist : 515
Digital Marketing Manager : 52
Director of Data Science : 57
Director of HR : 69
Director of Marketing : 88
Financial Analyst : 53
Financial Manager : 139
Front end Developer : 239
Front End Developer : 31
Full Stack Engineer : 304
HR Generalist : 104
Human Resources Coordinator : 50
Human Resources Manager : 152
Marketing Analyst : 144
Marketing Coordinator : 167
Marketing Director : 65
Marketing Manager : 315
Operations Manager : 122
Product Designer : 80
Product Manager : 323
Product Marketing Manager : 70
Project Engineer : 317
Project Manager : 34
Receptionist : 57
Research Director : 75
Research Scientist : 119
Sales Associate : 212
Sales Director : 62
Sales Executive : 38
Sales Manager : 58
Sales Representative : 81
Software Developer : 186
Software Engineer : 809
Software Engineer Manager : 376
Web Developer : 129
Im Folgenden werden Jobs auf Basis Ihrer Jobtitel in technische und adminisztrative Kategoerien unterteilt. Danach werden die Datensätze “technische_jobs” und “admin_Jobs” erstellt.
# Filtern nach technischen Jobs
technische_jobs <- filtered_data[grep("data|engineer|developer|analyst|scientist", tolower(filtered_data$Job.Title)), ]
# Filtern nach wirtschaftlichen/administrativen Jobs
admin_jobs <- filtered_data[grep("associate|director|manager|sales|coordinator|generalist|receptionist|designer", tolower(filtered_data$Job.Title)), ]
# Beispiel für die Ausgabe der ersten paar Zeilen der gefilterten Daten
head(technische_jobs)# A tibble: 6 × 11
Job.Title job_count Age Gender Education.Level Years.Of.Experience Salary
<chr> <int> <dbl> <chr> <dbl> <dbl> <dbl>
1 Back end De… 242 33 Female 2 5 110000
2 Back end De… 242 32 Male 1 4 95000
3 Back end De… 242 26 Female 2 3 90000
4 Back end De… 242 26 Female 2 2 70000
5 Back end De… 242 24 Female 1 1 60000
6 Back end De… 242 26 Female 2 3 90000
# ℹ 4 more variables: Country <chr>, Race <chr>, Senior <dbl>, SalaryKat <fct>
head(admin_jobs)# A tibble: 6 × 11
Job.Title job_count Age Gender Education.Level Years.Of.Experience Salary
<chr> <int> <dbl> <chr> <dbl> <dbl> <dbl>
1 Content Mar… 73 30 Female 1 3 55000
2 Content Mar… 73 27 Female 2 4 80000
3 Content Mar… 73 27 Female 2 4 80000
4 Content Mar… 73 27 Female 2 4 80000
5 Content Mar… 73 27 Female 2 4 80000
6 Content Mar… 73 27 Female 2 4 80000
# ℹ 4 more variables: Country <chr>, Race <chr>, Senior <dbl>, SalaryKat <fct>
# Anzahl der technischen Jobs
anzahl_technische_jobs <- nrow(technische_jobs)
cat("Anzahl der technischen Jobs:", anzahl_technische_jobs, "\n")Anzahl der technischen Jobs: 3912
# Anzahl der administrativen Jobs
anzahl_admin_jobs <- nrow(admin_jobs)
cat("Anzahl der administrativen Jobs:", anzahl_admin_jobs, "\n")Anzahl der administrativen Jobs: 2919
Zu erkennen ist hier, dass es detlich mehr technische Jobs als administraive Jobs in diesem Datensatz gibt.
???? notwenig nochmal zu wissen wie viele insgesamt in filtered data sind????
# Anzahl der Zeilen (Werte) in filtered_data
anzahl_werte_filtered_data <- nrow(filtered_data)
# Anzeigen der Anzahl der Werte
cat("Anzahl der Werte in filtered_data:", anzahl_werte_filtered_data, "\n")Anzahl der Werte in filtered_data: 6398
Insgesamt gibt es in dem gefilterten Datensatz 6398 Einträge, was bedeutet, dass ungefähr 60% technische Jobs und 40% administrative Jobs sind.
Nun wird nochmal der gefilterte Datensatz mit der Summe von “anzahl_technische_jobs” und “anzahl_administraive Jobs” verglichen.
anzahl_jobs <- anzahl_technische_jobs + anzahl_admin_jobs
cat(anzahl_jobs)6831
Zu erkennen ist hier, dass es zwei unterschiedliche Werte für die Beiden Datensätze gibt.
Wir vermuten, dass einige Jobs doppelt gezählt werden. Dies würde erklären, dass deutlich mehr Einträge in “anzahl_jobs” sind. Unser Lösungsvorschlag wäre hier, dass wir den Jobs IDs geben.
Der erste Lösungsansatz sieht wie folgt aus:
Die jobs werden basierend auf bestimmten Namen ( data, enginner etc. ) gefiltert und alle Duplikate werden entfernt. Das Ergebnis ist nun ein neuer Datensatz “filtered_data_neu” in dem alle Zeilen außer, die technischen und administrativen Jobs enthält.
# Add ID-Spalte
filtered_data$ID <- 1:nrow(filtered_data)
# Filtern nach technischen Jobs und Entfernen von Duplikaten
technische_jobs2 <- unique(filtered_data[grep("data|engineer|developer|analyst|scientist", tolower(filtered_data$Job.Title)), ])
# Filtern nach wirtschaftlichen/administrativen Jobs und Entfernen von Duplikaten
admin_jobs2 <- unique(filtered_data[grep("associate|director|manager|sales|coordinator|generalist|receptionist|designer", tolower(filtered_data$Job.Title)), ])
# Merke die IDs der übereinstimmenden Zeilen
ids_technische_jobs <- filtered_data$ID[filtered_data$Job.Title %in% technische_jobs2$Job.Title]
ids_admin_jobs <- filtered_data$ID[filtered_data$Job.Title %in% admin_jobs2$Job.Title]
# Entferne die entsprechenden Zeilen aus filtered_data
filtered_data_neu <- filtered_data[!(filtered_data$ID %in% c(ids_technische_jobs, ids_admin_jobs)), ]
# Beispiel für die Ausgabe der ersten paar Zeilen der gefilterten Daten
head(filtered_data_neu)# A tibble: 0 × 12
# ℹ 12 variables: Job.Title <chr>, job_count <int>, Age <dbl>, Gender <chr>,
# Education.Level <dbl>, Years.Of.Experience <dbl>, Salary <dbl>,
# Country <chr>, Race <chr>, Senior <dbl>, SalaryKat <fct>, ID <int>
anzahl_technische_jobs2 <- nrow(technische_jobs2)
cat("Anzahl der technischen Jobs2:", anzahl_technische_jobs2, "\n")Anzahl der technischen Jobs2: 3912
anzahl_admin_jobs2 <- nrow(admin_jobs2)
cat("Anzahl der administrativen Jobs2:", anzahl_admin_jobs2, "\n")Anzahl der administrativen Jobs2: 2919
Es scheint als würde dieser Lösungsansatz nicht funktionieren, da der neue Datensatz keine Einträge enthält.
Der zweite Lösungsansatz zum Problem der Klassifizierung der verschiedenen Job Typen kann gelöst werden, indem die beiden Jobs nicht in 2 Tabellen unterteilt werden, sondern Jede Zeile einen Wert des entsprechenden Jobs Typs zugeordnet wird.
# Erstellung der neuen Spalte "job_type" basierend auf den gegebenen Filtern
filtered_data$job_type <- ifelse(
grepl("data|engineer|developer|analyst|scientist", tolower(filtered_data$Job.Title)),
0, # 0 für technische Jobs
ifelse(
grepl("associate|director|manager|sales|coordinator|generalist", tolower(filtered_data$Job.Title)),
1, # 1 für administrative Jobs
NA # NA für alle anderen
)
)
# Anzeige der Anzahl aller Zeilen im Datensatz und der Anzahl der Zeilen für jede job_type-Ausprägung
total_rows <- nrow(filtered_data)
count_job_types <- table(filtered_data$job_type, useNA = "ifany")
# Ausgabe der Ergebnisse
print(paste("Gesamtanzahl der Zeilen im Datensatz:", total_rows))[1] "Gesamtanzahl der Zeilen im Datensatz: 6398"
print("Anzahl der Zeilen für jede job_type-Ausprägung:")[1] "Anzahl der Zeilen für jede job_type-Ausprägung:"
print(count_job_types)
0 1 <NA>
3912 2349 137
Es wird eine neue Spalte namens “job_type” erstellt. Diese Spalte wird nun mit Werten gefüllt. Der Wert = für Zielen mit technischen Jobs und 1 für administrative Jobs. NA erhalten alle anderen Jobtypen.
Nun werden die eben gefundenen NAs in einen neuen Datensatz geschrieben.
# Auswahl aller Zeilen mit NA-Werten in der Spalte "job_type"
na_job_type_rows <- subset(filtered_data, is.na(job_type))
# Anzeige der ausgewählten Zeilen mit NA in "job_type"
na_job_type_rows# A tibble: 137 × 13
Job.Title job_count Age Gender Education.Level Years.Of.Experience Salary
<chr> <int> <dbl> <chr> <dbl> <dbl> <dbl>
1 Product De… 80 33 Male 2 6 90000
2 Product De… 80 43 Female 3 18 140000
3 Product De… 80 45 Female 3 15 150000
4 Product De… 80 45 Female 3 15 150000
5 Product De… 80 44 Female 3 15 150000
6 Product De… 80 44 Female 3 15 150000
7 Product De… 80 27 Male 1 3 60000
8 Product De… 80 27 Male 1 3 60000
9 Product De… 80 27 Male 1 3 60000
10 Product De… 80 27 Male 1 3 60000
# ℹ 127 more rows
# ℹ 6 more variables: Country <chr>, Race <chr>, Senior <dbl>, SalaryKat <fct>,
# ID <int>, job_type <dbl>
Das Ergebnis dieser Abfrage ist eine Tabelle, welche nur aus Product Designer % Receptionist besteht. Diese werden nun den adminsitrativen Jobs hinzugefügt.
# Aktualisierung der job_type-Spalte für die spezifischen Job-Titel
filtered_data$job_type[filtered_data$Job.Title %in% c("Product Designer", "Receptionist")] <- 1
# Anzeige der aktualisierten Daten für die ausgewählten Job-Titel
subset(filtered_data, Job.Title %in% c("Product Designer", "Receptionist"))# A tibble: 137 × 13
Job.Title job_count Age Gender Education.Level Years.Of.Experience Salary
<chr> <int> <dbl> <chr> <dbl> <dbl> <dbl>
1 Product De… 80 33 Male 2 6 90000
2 Product De… 80 43 Female 3 18 140000
3 Product De… 80 45 Female 3 15 150000
4 Product De… 80 45 Female 3 15 150000
5 Product De… 80 44 Female 3 15 150000
6 Product De… 80 44 Female 3 15 150000
7 Product De… 80 27 Male 1 3 60000
8 Product De… 80 27 Male 1 3 60000
9 Product De… 80 27 Male 1 3 60000
10 Product De… 80 27 Male 1 3 60000
# ℹ 127 more rows
# ℹ 6 more variables: Country <chr>, Race <chr>, Senior <dbl>, SalaryKat <fct>,
# ID <int>, job_type <dbl>
# Anzeige der Anzahl aller Zeilen im Datensatz und der Anzahl der Zeilen für jede job_type-Ausprägung
total_rows <- nrow(filtered_data)
count_job_types <- table(filtered_data$job_type, useNA = "ifany")
# Ausgabe der Ergebnisse
print(paste("Gesamtanzahl der Zeilen im Datensatz:", total_rows))[1] "Gesamtanzahl der Zeilen im Datensatz: 6398"
print("Anzahl der Zeilen für jede job_type-Ausprägung:")[1] "Anzahl der Zeilen für jede job_type-Ausprägung:"
print(count_job_types)
0 1
3912 2486
Nun ist das Klassifizierungsproblem gelöst. Aufgrund dessen können jetzt auch Diegramme über Job_types ausgewertet werden.
Im folgenden werden einige Boxplots erstellt.
ggplot(filtered_data, aes(x = factor(Gender), y = Years.Of.Experience, fill = Gender)) +
geom_boxplot(alpha = 0.7) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Boxplot: Years of Experience vs. Gender",
x = "Gender",
y = "Years of Experience",
fill = "Gender") +
theme_minimal()# Bibliothek ggplot2 laden
library(ggplot2)
# Daten für China filtern
data_china <- subset(filtered_data, Country == "China")
# Boxplot erstellen
ggplot(data_china, aes(x = factor(Gender), y = Years.Of.Experience, fill = Gender)) +
geom_boxplot(alpha = 0.7) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Boxplot: Years of Experience vs. Gender (China)",
x = "Gender",
y = "Years of Experience",
fill = "Gender") +
theme_minimal()# Bibliothek ggplot2 laden
library(ggplot2)
# Daten für USA filtern
data_usa <- subset(filtered_data, Country == "USA")
# Boxplot erstellen
ggplot(data_usa, aes(x = factor(Gender), y = Years.Of.Experience, fill = Gender)) +
geom_boxplot(alpha = 0.7) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Boxplot: Years of Experience vs. Gender (USA)",
x = "Gender",
y = "Years of Experience",
fill = "Gender") +
theme_minimal()# Bibliothek ggplot2 laden
library(ggplot2)
# Daten für China filtern
data_china <- subset(filtered_data, Country == "China")
# Boxplot für Salary vs. Gender erstellen
ggplot(data_china, aes(x = factor(Gender), y = Salary, fill = Gender)) +
geom_boxplot(alpha = 0.7) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Boxplot: Salary vs. Gender (China)",
x = "Gender",
y = "Salary",
fill = "Gender") +
theme_minimal()# Bibliothek ggplot2 laden
library(ggplot2)
# Daten für USA filtern
data_usa <- subset(filtered_data, Country == "USA")
# Boxplot für Salary vs. Gender erstellen
ggplot(data_usa, aes(x = factor(Gender), y = Salary, fill = Gender)) +
geom_boxplot(alpha = 0.7) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Boxplot: Salary vs. Gender (USA)",
x = "Gender",
y = "Salary",
fill = "Gender") +
theme_minimal()Nach intensiven Vergleichen der Grafiken bezüglich der Salary stellt sich nun die Frage:
Ist die Gender-Pay-Gap in China doch Größer, da der größte Faktor für die Salary die Years of Experience sind?
# Korrelation zwischen Salary und Years.Of.Experience berechnen
correlation_salary_experience <- cor(filtered_data$Salary, filtered_data$Years.Of.Experience)
# Ausgabe des Ergebnisses
cat("Die Korrelation zwischen Salary und Years.Of.Experience ist:", correlation_salary_experience, "\n")Die Korrelation zwischen Salary und Years.Of.Experience ist: 0.8103542
# Korrelation zwischen Salary und Education.Level berechnen
correlation_salary_education <- cor(filtered_data$Salary, filtered_data$Education.Level, use = "complete.obs")
# Ausgabe des Ergebnisses
cat("Die Korrelation zwischen Salary und Education.Level ist:", correlation_salary_education, "\n")Die Korrelation zwischen Salary und Education.Level ist: 0.6374551
# Korrelation zwischen Salary und Age berechnen
correlation_salary_age <- cor(filtered_data$Salary, filtered_data$Age, use = "complete.obs")
# Ausgabe des Ergebnisses
cat("Die Korrelation zwischen Salary und Age ist:", correlation_salary_age, "\n")Die Korrelation zwischen Salary und Age ist: 0.7291603
# Korrelation zwischen Years.Of.Experience und Age berechnen
correlation_experience_age <- cor(filtered_data$Years.Of.Experience, filtered_data$Age, use = "complete.obs")
# Ausgabe des Ergebnisses
cat("Die Korrelation zwischen Years.Of.Experience und Age ist:", correlation_experience_age, "\n")Die Korrelation zwischen Years.Of.Experience und Age ist: 0.9363709
# Annahme: "filtered_data" ist Ihr Datensatz
# Annahme: Die Spalten sind "Seniority" und "Years.Of.Experience"
# Korrelation zwischen Seniority und Years.Of.Experience berechnen
correlation_seniority_experience <- cor(filtered_data$Senior, filtered_data$Years.Of.Experience, use = "complete.obs")
# Ausgabe des Ergebnisses
cat("Die Korrelation zwischen Seniority und Years.Of.Experience ist:", correlation_seniority_experience, "\n")Die Korrelation zwischen Seniority und Years.Of.Experience ist: 0.3192657
Das Ergebnis der Korrelationen:
Von Salary und Years.of.experience ist es 0.81. Von Salary und Age ist es 0.73 und die von Age und Years.Of.Experience ist 0.93.
Nun stellt sich folgende Frage: Wie kommt es zu so einem großen Unterschied Zwischen den Werten im Vergleich zu Salary, obwohl sie doch eine starke Korrelation zueinadner haben?
Mögliche Antworten auf diese Frage wären:
Verteilung der Daten:
Es ist möglich, dass die Verteilung der Daten in den Variablen “Age” und “Years.Of.Experience” anders ist als in der Variable “Salary”. Wenn die Daten in “Age” und “Years.Of.Experience” breiter gestreut sind, kann dies zu einer geringeren Korrelation führen, selbst wenn eine starke lineare Beziehung besteht.
Nicht-lineare Beziehung:
Die Korrelation misst nur lineare Beziehungen. Wenn die Beziehung zwischen “Age” und “Years.Of.Experience” nicht linear ist, könnte dies zu einem niedrigeren Korrelationswert führen.
Ausreißer:
Das Vorhandensein von Ausreißern kann die Korrelation beeinflussen. Wenn es Ausreißer in einer der Variablen gibt, kann dies den Korrelationswert beeinträchtigen.
Stichprobengröße:
Bei kleineren Stichproben können Korrelationswerte instabiler sein.
# Filtern der Daten für technische und administrative Jobs basierend auf den Kriterien
technische_jobs <- subset(filtered_data, job_type == 0)
admin_jobs <- subset(filtered_data, job_type == 1)
# Durchschnittliche Gehälter pro Jobtyp für technische Jobs berechnen
average_salaries_technical <- mean(technische_jobs$Salary, na.rm = TRUE)
# Durchschnittliche Gehälter pro Jobtyp für administrative Jobs berechnen
average_salaries_admin <- mean(admin_jobs$Salary, na.rm = TRUE)
# Zusammenführen der durchschnittlichen Gehälter in einem Datenrahmen
all_average_salaries <- data.frame(Job.Type = c("technisch", "admin"),
Average.Salary = c(average_salaries_technical, average_salaries_admin))
# Erstellung des Diagramms mit angepasster Achsenbeschriftung
ggplot(all_average_salaries, aes(x = Job.Type, y = Average.Salary, fill = Job.Type)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.7) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Durchschnittliche Gehälter nach Jobtyp",
x = "Jobtyp",
y = "Durchschnittliches Gehalt") +
theme_minimal() +
scale_y_continuous(labels = scales::comma) # Verwendung von scales::comma für die Achsenbeschriftung in TausendenUnd das Obwohl in den Admin Jobs auch direktoren und Manager vertreten sind
Nehmen wir an das Manager ein Job Titel wie Projekt Manager ist und nicht Manager für Projekte und dieser Titel in unserem Datensatz Director ist? Thema Führungsposition
Unsere Untersuchungen haben Ergeben das wir die Daten für unsere Explorative Datenanalyse aber auch die Regression neu aufbereiten müssen.
Dazu suchen wir:
Unterscheidet sich ein native und expat im jeweiligen Land? Welche Annahmen sind dafür nötig? Hier die Annahme das “White” generell nicht ausgewandert ist da wir hier Länder mit ähnlicher Kultur und Salary haben.
ggplot(filtered_data, aes(x = Country, fill = Race)) +
geom_bar(position = "dodge") +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Count of Races in Each Country",
x = "Country",
y = "Count") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))Dafür wird eine neue Spalte eingefügt, die mit numerischen werten arbeitet. 0 steht für Einheimische und 1 für einen Expat. Den Wert null erhalten alle zeilen bei denen wir folgende Übereinstimmung feststellen: African American (USA) White (Canada, USA, UK, Australia) Chinese (China) Australian(Australia) Welsh (UK) jede andere Race ist ja dementsprechend Expat und erhält eine 1 in der Spalte Expat.
????????
# Erstellen der Spalte "Expat" basierend auf den angegebenen Kriterien
filtered_data$Expat <- 0 # Standardwert 0 (Einheimische)
# Festlegen von Bedingungen für Expats basierend auf Land und Ethnizität
expat_conditions <- list(
filtered_data$Race == "African American" & filtered_data$Country == "USA",
filtered_data$Race %in% c("White", "Chinese", "Australian", "Welsh") &
filtered_data$Country %in% c("Canada", "USA", "UK", "Australia"),
TRUE # Für alle anderen Rassen (Expat)
)
# Setzen von Werten entsprechend den Bedingungen
filtered_data$Expat <- ifelse(expat_conditions[[1]] | expat_conditions[[2]], 0,
ifelse(expat_conditions[[3]], 1, NA))
# Anzeige des aktualisierten Datensatzes zur Überprüfung
head(filtered_data)# A tibble: 6 × 14
Job.Title job_count Age Gender Education.Level Years.Of.Experience Salary
<chr> <int> <dbl> <chr> <dbl> <dbl> <dbl>
1 Back end De… 242 33 Female 2 5 110000
2 Back end De… 242 32 Male 1 4 95000
3 Back end De… 242 26 Female 2 3 90000
4 Back end De… 242 26 Female 2 2 70000
5 Back end De… 242 24 Female 1 1 60000
6 Back end De… 242 26 Female 2 3 90000
# ℹ 7 more variables: Country <chr>, Race <chr>, Senior <dbl>, SalaryKat <fct>,
# ID <int>, job_type <dbl>, Expat <dbl>
Ausgabe hier sind nun die ersten Zeilen der aktualisierten Version von “filtered_data” indem die Spalte “Expat” basierend auf den oben genannten Kriterien befüllt wurde.
Aus den überlegungen der Tests und der Vorarbeit wurden folgende Thesen formuliert. Dieser Abschnitt wird in unterschiedliche Teilabschnitte geteilt.
ggplot(filtered_data, aes(x = factor(Gender), y = Salary, fill = Gender)) +
geom_boxplot(alpha = 0.7) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Boxplot: Salary vs. Gender",
x = "Gender",
y = "Salary",
fill = "Gender") +
theme_minimal()Mithilfe des Boxplots ist zu erkennen, das der Median deutlich höher bei den Männer ist. Daraus lässt sich Schlussfolgern, dass die These Korrekt ist.
# Daten für USA filtern
data_usa <- subset(filtered_data, Country == "USA")
# Boxplot erstellen
ggplot(data_usa, aes(x = factor(Gender), y = Years.Of.Experience, fill = Gender)) +
geom_boxplot(alpha = 0.7) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Boxplot: Years of Experience vs. Gender (USA)",
x = "Gender",
y = "Years of Experience",
fill = "Gender") +
theme_minimal()# Daten für China filtern
data_china <- subset(filtered_data, Country == "China")
# Boxplot für Salary vs. Gender erstellen
ggplot(data_china, aes(x = factor(Gender), y = Salary, fill = Gender)) +
geom_boxplot(alpha = 0.7) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Boxplot: Salary vs. Gender (China)",
x = "Gender",
y = "Salary",
fill = "Gender") +
theme_minimal()These Korrekt
ggplot(filtered_data, aes(x = factor(Gender), y = Years.Of.Experience, fill = Gender)) +
geom_boxplot(alpha = 0.7) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Boxplot: Years of Experience vs. Gender",
x = "Gender",
y = "Years of Experience",
fill = "Gender") +
theme_minimal()# Daten für China filtern
data_china <- subset(filtered_data, Country == "China")
# Boxplot erstellen
ggplot(data_china, aes(x = factor(Gender), y = Years.Of.Experience, fill = Gender)) +
geom_boxplot(alpha = 0.7) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Boxplot: Years of Experience vs. Gender (China)",
x = "Gender",
y = "Years of Experience",
fill = "Gender") +
theme_minimal()# Daten für USA filtern
data_usa <- subset(filtered_data, Country == "USA")
# Boxplot erstellen
ggplot(data_usa, aes(x = factor(Gender), y = Years.Of.Experience, fill = Gender)) +
geom_boxplot(alpha = 0.7) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Boxplot: Years of Experience vs. Gender (USA)",
x = "Gender",
y = "Years of Experience",
fill = "Gender") +
theme_minimal()ist jetzt der gender pay gap in china doch größer da der größte faktor für salary years of experience ist?
Dafür schauen wir uns an: Korrelation von years of experience und salary
# Korrelation zwischen Salary und Years.Of.Experience berechnen
correlation_salary_experience <- cor(filtered_data$Salary, filtered_data$Years.Of.Experience)
# Ausgabe des Ergebnisses
cat("Die Korrelation zwischen Salary und Years.Of.Experience ist:", correlation_salary_experience, "\n")Die Korrelation zwischen Salary und Years.Of.Experience ist: 0.8103542
These Korrekt da es im gleichen Verhältnis steht. Nicht desto trotz gibt es einen Unterschied zwischen den Geschlechtern bei gleichbleibender Arbeitderfahrung was auf einen kleinen Gender Pay gap schließen lässt.
Unsere Untersuchungen haben Ergeben das wir die Daten für unsere Explorative Datenanalyse aber auch die Regression neu aufbereiten müssen.
Dazu suchen wir: unterscheide ich einen native und expat im jeweiligen Land. Welche annahmen sind dafür nötig? Hier die annahme das White generell nicht ausgewandert ist da wir hier länder mit ähnlicher kultur und salary haben
ggplot(filtered_data, aes(x = Country, fill = Race)) +
geom_bar(position = "dodge") +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Count of Races in Each Country",
x = "Country",
y = "Count") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))Daten für Analyse von Expats und Einheimischen bereits Aufbereitet. Bitte hier nochmal beschrieben
# Erstellung des Boxplots für Expats und Einheimische
ggplot(filtered_data, aes(x = as.factor(Expat), y = Salary, fill = factor(Expat))) +
geom_boxplot() +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Vergleich der Gehälter von Expats und Einheimischen",
x = "Expat",
y = "Gehalt") +
scale_x_discrete(labels = c("Einheimische (0)", "Expats (1)")) +
theme_minimal()# Mittelwert der Gehälter für Expats (Expat = 1)
mean_salary_expat <- mean(filtered_data$Salary[filtered_data$Expat == 1], na.rm = TRUE)
mean_salary_expat[1] 116928.1
# Mittelwert der Gehälter für Einheimische (Expat = 0)
mean_salary_native <- mean(filtered_data$Salary[filtered_data$Expat == 0], na.rm = TRUE)
mean_salary_native[1] 116572.5
These verworfen. Da es offensichtlich keine Unterschiede gibt. Ggf. noch einzelne Ethnizitäten betrachten
Ohne ein Mindestmaß ein Bildung ist keine weitere Gehaltsentwicklung möglich
Bildungsniveau Codes:
0 = High School Abschluss
1 = Bachelor
2 = Master
3 = Doctor
Deskriptive Statistiken: Man könnte Quantile oder Perzentile des Gehalts für jedes Bildungsniveau berechnen. Dies bietet einen Überblick über die Verteilung der Gehälter und zeigt potenzielle Grenzwerte auf.
Boxplots pro Bildungsniveau: Man könnte Boxplots für jedes Bildungsniveau erstellen, um die Verteilung der Gehälter visuell zu vergleichen. Dies kann helfen, Ausreißer und Unterschiede zwischen den Bildungsniveaus zu identifizieren.
Visualisierungen: Verschiedene Visualisierungen wie Scatterplots oder Liniendiagramme könnten erstellt werden, um Trends oder Muster zwischen Gehalt und Bildungsniveau zu erkennen.
# Bibliotheken laden
library(ggplot2)
library(dplyr)
# Daten berechnen
salary_percentiles <- filtered_data %>%
group_by(Education.Level) %>%
summarise(`10th Percentile` = quantile(Salary, probs = 0.1, na.rm = TRUE),
`25th Percentile` = quantile(Salary, probs = 0.25, na.rm = TRUE),
`50th Percentile (Median)` = quantile(Salary, probs = 0.5, na.rm = TRUE),
`75th Percentile` = quantile(Salary, probs = 0.75, na.rm = TRUE),
`90th Percentile` = quantile(Salary, probs = 0.9, na.rm = TRUE))
# Reshape der Daten für das Plotting
salary_percentiles_long <- salary_percentiles %>%
tidyr::pivot_longer(cols = -Education.Level, names_to = "Percentile", values_to = "Salary")
# Diagramm erstellen
ggplot(salary_percentiles_long, aes(x = Education.Level, y = Salary, fill = Percentile)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.7) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Perzentile des Gehalts nach Bildungsniveau",
x = "Bildungsniveau",
y = "Gehalt",
fill = "Perzentil") +
theme_minimal()# Berechnung der Durchschnittsgehälter pro Bildungsniveau
average_salary_education <- aggregate(Salary ~ Education.Level, data = filtered_data, FUN = mean, na.rm = TRUE)
# Anzeige der Durchschnittsgehälter pro Bildungsniveau
average_salary_education Education.Level Salary
1 0 34511.62
2 1 97042.04
3 2 129983.77
4 3 165796.75
# Bildungsniveau nach aufsteigender Reihenfolge sortieren
filtered_data <- filtered_data %>%
mutate(Education.Level = factor(Education.Level, levels = unique(sort(Education.Level))))
# Boxplot erstellen
ggplot(filtered_data, aes(x = reorder(factor(Education.Level), Salary, FUN = median), y = Salary)) +
geom_boxplot(color = "black", fill = viridis(2)[2]) +
labs(title = "Boxplot des Gehalts nach Bildungsniveau",
x = "Bildungsniveau",
y = "Gehalt") +
theme_minimal()# Bildungsniveau nach aufsteigender Reihenfolge sortieren
filtered_data <- filtered_data %>%
mutate(Education.Level = factor(Education.Level, levels = unique(sort(Education.Level))))
# Scatterplot erstellen
ggplot(filtered_data, aes(x = reorder(factor(Education.Level), Salary, FUN = median), y = Salary)) +
geom_point() +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Gehalt nach Bildungslevel",
x = "Bildungslevel",
y = "Gehalt") +
theme_minimal()# Bildungslevel neu ordnen
filtered_data$Education.Level <- factor(filtered_data$Education.Level, levels = c("0", "1", "2", "3"))
# Liniendiagramm mit umgekehrter Reihenfolge des Bildungsniveaus erstellen
ggplot(filtered_data, aes(x = Education.Level, y = Salary, group = 1)) +
geom_line() +
stat_summary(fun.y = median, geom = "point", size = 3, color = "red") +
labs(title = "Gehalt nach Bildungslevel",
x = "Bildungslevel",
y = "Gehalt") +
theme_minimal()Warning: The `fun.y` argument of `stat_summary()` is deprecated as of ggplot2 3.3.0.
ℹ Please use the `fun` argument instead.
hier diagramm erklären weil anderes aus vorlesung (Liniendiagramm)
These Korrekt: Ohne einen Hochschulabschluss gibt es eine Gehaltsgrenze. Die top 90% ohne Hochschulabschluss fangen bei den unteren 10% mit Hochschulabschluss an aus der Sicht des Gehalts.
Unter dem Punkt Datenaufbereitung wurden zu diesem Zweck alle Jobs in Administrativ und Technisch eingeteilt. Zudem filtern wir noch zusätzlich alle Datensätze raus welche “Director” im Jobtitel enthalten da diese sich nicht auf eine der Kategorisieren eindeutig zuordnen lassen. (z.B. Engineering Director)
# Kopie von filtered_data als filtered_data2 erstellen
filtered_data2 <- filtered_data
# Filtern der Daten für Jobs mit "Director" im Jobtitel in filtered_data2
director_jobs <- filtered_data2 %>%
filter(grepl("Director", Job.Title))
# Entfernen der Zeilen mit "Director" aus filtered_data2
filtered_data2 <- filtered_data2 %>%
anti_join(director_jobs)Joining with `by = join_by(Job.Title, job_count, Age, Gender, Education.Level,
Years.Of.Experience, Salary, Country, Race, Senior, SalaryKat, ID, job_type,
Expat)`
nrow(director_jobs)[1] 416
nrow(filtered_data)[1] 6398
nrow(filtered_data2)[1] 5982
Eine Übersicht der durchschnittlichen Gehälter nach der Sortierung von Jobs technischer und adminsitrativer Natur.
# Filtern der Daten für technische und administrative Jobs basierend auf den Kriterien
technische_jobs <- subset(filtered_data, job_type == 0)
admin_jobs <- subset(filtered_data, job_type == 1)
# Durchschnittliche Gehälter pro Jobtyp für technische Jobs berechnen
average_salaries_technical <- mean(technische_jobs$Salary, na.rm = TRUE)
# Durchschnittliche Gehälter pro Jobtyp für administrative Jobs berechnen
average_salaries_admin <- mean(admin_jobs$Salary, na.rm = TRUE)
# Zusammenführen der durchschnittlichen Gehälter in einem Datenrahmen
all_average_salaries <- data.frame(Job.Type = c("technisch", "admin"),
Average.Salary = c(average_salaries_technical, average_salaries_admin))
# Erstellung des Diagramms mit angepasster Achsenbeschriftung
ggplot(all_average_salaries, aes(x = Job.Type, y = Average.Salary, fill = Job.Type)) +
geom_bar(stat = "identity", position = "dodge", alpha = 0.7) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Durchschnittliche Gehälter nach Jobtyp",
x = "Jobtyp",
y = "Durchschnittliches Gehalt") +
theme_minimal() +
scale_y_continuous(labels = scales::comma) # Verwendung von scales::comma für die Achsenbeschriftung in Tausenden# Filtern der Daten für technische und administrative Jobs basierend auf den Kriterien
technische_jobs <- subset(filtered_data, job_type == 0)
admin_jobs <- subset(filtered_data, job_type == 1)
# Durchschnittliche Gehälter pro Jobtyp für technische Jobs berechnen
average_salaries_technical <- mean(technische_jobs$Salary, na.rm = TRUE)
# Durchschnittliche Gehälter pro Jobtyp für administrative Jobs berechnen
average_salaries_admin <- mean(admin_jobs$Salary, na.rm = TRUE)
# Ausgabe der berechneten durchschnittlichen Gehälter mit Beschriftung
cat("Durchschnittliches Gehalt für technische Jobs:", average_salaries_technical, "\n")Durchschnittliches Gehalt für technische Jobs: 126441.3
cat("Durchschnittliches Gehalt für administrative Jobs:", average_salaries_admin, "\n")Durchschnittliches Gehalt für administrative Jobs: 101595.3
Eine Übersicht der durchschnittlichen Gehälter nach der Sortierung von Jobs technischer und adminsitrativer Natur aller Länder.
#Daten nach Bildungsniveau, Land und Median des Gehalts gruppieren
summary_data <- aggregate(Salary ~ Education.Level + Country, data = filtered_data, FUN = median)
#Balkendiagramm erstellen
ggplot(summary_data, aes(x = Education.Level, y = Salary, fill = Country)) +
geom_bar(stat = "identity", position = "dodge") +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Median Gehalt nach Bildungslevel und Land",
x = "Bildungslevel",
y = "Median Gehalt",
fill = "Land") +
theme_minimal()These Korrekt, und das Obwohl in den Admin Jobs auch in seltenen Fällen Manager vertreten sind.
Es lässt sich zudem beobachten das Doktoren in Australien ein höheres Gehalt verdienen als in anderen Ländern.
Data Scientist ist laut dem Harvard Business Review der “Sexiest Job of the 21st Century”*
*https://hbr.org/2012/10/data-scientist-the-sexiest-job-of-the-21st-century
Bei dieser Aussage haben wir uns gefragt ob sich die Beschreibung auch auf die Gehälter des Data Scientists anwenden lässt.
Rausfiltern aller Manager und Direktoren. Einengung nach Jobs mit dem Stichwort Data, Software, Developer und Engineer
# Filtern der Daten für Jobs mit spezifischen Schlüsselwörtern im Jobtitel
filtered_data3 <- filtered_data %>%
filter(grepl("Data|Software|Developer|Engineer", Job.Title))
# Anzeigen aller eindeutigen Jobtitel und deren Häufigkeit in filtered_data3
job_title_count <- table(filtered_data3$Job.Title)
job_title_df <- data.frame(Job_Title = names(job_title_count), Frequency = as.numeric(job_title_count))
# Anzeige des Dataframes mit den Jobtiteln und deren Häufigkeit
job_title_df Job_Title Frequency
1 Back end Developer 242
2 Data Analyst 391
3 Data Scientist 515
4 Director of Data Science 57
5 Front end Developer 239
6 Front End Developer 31
7 Full Stack Engineer 304
8 Project Engineer 317
9 Software Developer 186
10 Software Engineer 809
11 Software Engineer Manager 376
12 Web Developer 129
# Filtern der Daten für Jobs ohne "Director" im Jobtitel
filtered_data3 <- filtered_data3 %>%
filter(!grepl("Director|Manager", Job.Title))
# Anzeigen aller eindeutigen Jobtitel und deren Häufigkeit in filtered_data3
job_title_count <- table(filtered_data3$Job.Title)
job_title_df <- data.frame(Job_Title = names(job_title_count), Frequency = as.numeric(job_title_count))
# Anzeige des Dataframes mit den Jobtiteln und deren Häufigkeit
job_title_df Job_Title Frequency
1 Back end Developer 242
2 Data Analyst 391
3 Data Scientist 515
4 Front end Developer 239
5 Front End Developer 31
6 Full Stack Engineer 304
7 Project Engineer 317
8 Software Developer 186
9 Software Engineer 809
10 Web Developer 129
Aufteilen der Jobs in die Sparte Data & Software
# Erstellen der neuen Spalte 'data_job'
filtered_data3 <- filtered_data3 %>%
mutate(data_job = ifelse(grepl("Data", Job.Title), 1, 0))# Zählen der Anzahl von 0 und 1 in der Spalte data_job
count_0 <- sum(filtered_data3$data_job == 0, na.rm = TRUE)
count_1 <- sum(filtered_data3$data_job == 1, na.rm = TRUE)
# Ausgabe der Anzahl von 0 und 1
cat("Anzahl der Zeilen mit dem Wert 0 bei data_job (Data Scientists & Engineers):", count_0, "\n")Anzahl der Zeilen mit dem Wert 0 bei data_job (Data Scientists & Engineers): 2257
cat("Anzahl der Zeilen mit dem Wert 1 bei data_job (Software Engineers & Co):", count_1, "\n")Anzahl der Zeilen mit dem Wert 1 bei data_job (Software Engineers & Co): 906
Hierzu teilen wir noch die Arbeitserfahrung in Quartile ein für eine bessere Übersicht. Die Bildungsniveaus sind ja bereits in 4 Werte Unterteilt.
Ich weiß nicht warum aber ich muss das Paket neu laden.
# Berechnung der Quartile der Berufserfahrung
filtered_data4 <- filtered_data3 %>%
mutate(Experience_Quartile = ntile(Years.Of.Experience, 4))
# Balkendiagramm für data_job im Vergleich zum Gehalt
ggplot(filtered_data4, aes(x = factor(data_job), y = Salary)) +
stat_summary(fun = "mean", geom = "bar", position = "dodge", fill = viridis(2)[1]) +
labs(title = "Gehalt nach Data Job",
x = "Data Job",
y = "Gehalt (Mittelwert)")# Erstellung des Balkendiagramms
ggplot(filtered_data4, aes(y = Salary, x = factor(Experience_Quartile))) +
geom_bar(stat = "identity", position = "dodge", aes(fill = factor(data_job))) +
labs(title = "Quartile der Berufserfahrung nach Gehalt und Jobtyp",
x = "Quartile der Berufserfahrung",
y = "Gehalt") +
theme_minimal() +
scale_fill_viridis(discrete = TRUE)Bildungsniveau Codes:
0 = High School Abschluss
1 = Bachelor
2 = Master
3 = Doctor
# Erstellung des Balkendiagramms für Education Level
ggplot(filtered_data4, aes(y = Salary, x = factor(Education.Level))) +
geom_bar(stat = "identity", position = "dodge", aes(fill = factor(data_job))) +
scale_fill_viridis(discrete = TRUE) +
labs(title = "Quartile des Bildungsniveaus nach Gehalt und Jobtyp",
x = "Quartile des Bildungsniveaus",
y = "Gehalt") +
theme_minimal()Im Mittel verdienen Data Scientists mehr als Arbeitnehmer aus der Software Engineering & Co Gruppe. Wobei man beobachten kann das die Gruppe Data mindestens einen Bachelor bestitzt und erst ab dem Master mehr verdient als ihr counterpart. Bezüglich der Berufserfahrung lässt sich feststellen das es in jedem Quartil ein höheres Gehaltsniveau bei der Gruppe Data gibt.
Da dieses Land staistisch mehr Geld pro Einwohner erwirtschaftet.
BIP pro Kopf (2023):
Australien 64.813,85 US-Dollar Quelle: Australien - BIP pro Kopf bis 2028 | Statista
Canada 53.246,98 US-Dollar Quelle: Kanada - BIP pro Kopf bis 2028 | Statista
China 12.541,40 US-Dollar Quelle: China - BIP pro Kopf bis 2028 | Statista
UK 48.912,78 US-Dollar Quelle: Großbritannien - BIP pro Kopf bis 2028 | Statista
USA 76.343 US-Dollar Quelle: USA - BIP pro Kopf bis 2028 | Statista
# Erstelle eine neue Spalte "BIP_Per_Person" mit NA-Werten
filtered_data3$BIP_Per_Person <- NA
# Weise den genannten Ländern die entsprechenden BIP-Werte zu
filtered_data3$BIP_Per_Person[filtered_data3$Country == "Australia"] <- 64813.85
filtered_data3$BIP_Per_Person[filtered_data3$Country == "Canada"] <- 53246.98
filtered_data3$BIP_Per_Person[filtered_data3$Country == "China"] <- 12541.40
filtered_data3$BIP_Per_Person[filtered_data3$Country == "UK"] <- 48912.78
filtered_data3$BIP_Per_Person[filtered_data3$Country == "USA"] <- 76343.00head(filtered_data3)# A tibble: 6 × 16
Job.Title job_count Age Gender Education.Level Years.Of.Experience Salary
<chr> <int> <dbl> <chr> <fct> <dbl> <dbl>
1 Back end De… 242 33 Female 2 5 110000
2 Back end De… 242 32 Male 1 4 95000
3 Back end De… 242 26 Female 2 3 90000
4 Back end De… 242 26 Female 2 2 70000
5 Back end De… 242 24 Female 1 1 60000
6 Back end De… 242 26 Female 2 3 90000
# ℹ 9 more variables: Country <chr>, Race <chr>, Senior <dbl>, SalaryKat <fct>,
# ID <int>, job_type <dbl>, Expat <dbl>, data_job <dbl>, BIP_Per_Person <dbl>
Hat funktioniert.
# Scatterplot mit Farbgebung nach Ländern und Mittelwerten einzeichnen
ggplot(filtered_data3, aes(x = BIP_Per_Person, y = Salary, color = Country)) +
geom_point() +
stat_summary(fun = mean, geom = "point", shape = 23, size = 3, fill = "black") +
labs(title = "Vergleich von Gehalt und BIP pro Person nach Ländern",
x = "BIP pro Person",
y = "Gehalt",
color = "Land") +
scale_color_viridis(discrete = TRUE)# Berechnung der Mittelwerte nach Land
mean_salaries_by_country <- filtered_data3 %>%
group_by(Country) %>%
summarise(mean_salary = mean(Salary, na.rm = TRUE))
# Ausgabe der Mittelwerte nach Land
mean_salaries_by_country# A tibble: 5 × 2
Country mean_salary
<chr> <dbl>
1 Australia 121610.
2 Canada 125466.
3 China 120395.
4 UK 123564.
5 USA 119265.
# Berechnung der Mittelwerte nach Land
mean_salaries_by_country <- filtered_data3 %>%
group_by(Country) %>%
summarise(mean_salary = mean(Salary, na.rm = TRUE),
BIP_Per_Person = first(BIP_Per_Person)) # Annahme: BIP-Pro-Person-Werte sind konstant für jedes Land
# Ausgabe der Mittelwerte nach Land mit BIP pro Person
mean_salaries_by_country# A tibble: 5 × 3
Country mean_salary BIP_Per_Person
<chr> <dbl> <dbl>
1 Australia 121610. 64814.
2 Canada 125466. 53247.
3 China 120395. 12541.
4 UK 123564. 48913.
5 USA 119265. 76343
cor(filtered_data3$Salary, filtered_data3$BIP_Per_Person, use = "complete.obs")[1] -0.00247782
Nun ohne China
# Erstellen des neuen Datensatzes ohne Einträge für China
filtered_data3_no_china <- filtered_data3 %>% filter(Country != "China")cor(filtered_data3_no_china$Salary, filtered_data3_no_china$BIP_Per_Person, use = "complete.obs")[1] -0.04303227
# Anzahl der Datensätze mit dem Land "China" im Datensatz filtered_data3_no_china
count_china <- filtered_data3_no_china %>% filter(Country == "China") %>% nrow()
count_china[1] 0
These nicht korrekt. Es scheint so als gibt es eine negative Korrelation zwischen dem Gehalt welches ein Arbeitnehmer erhält und dem BIP des jeweiligen Landes. Selbst wenn man China aus der Rechnung rausnimmt welches aufgrund der ungewöhnlich hohen Einwohnerzahl und diversen Wirtschaft (Sonderverwaltungszonen und Kommunismus*) einen sehr niedrigen BIP hat aber trotzdem hohe Gehälter.
Wurden eventuell im Datensatz eventuell bewusst Jobs mit hohem Gehalt gewählt? Oder Von Spezifischen Firmen die international Tätig sind und gut bezahlen?
*https://de.wikipedia.org/wiki/Politisches_System_der_Volksrepublik_China
QQ Plot der Residuen gibt extra Punkte DAML Folien 3 Seite 10. Hinweise 2.4 S.10
Prüfung der Modellprämissen Punkt 5. Prüfung ist erklärt im Regressionsbeispiel im fall Umfangreiches Beispiel mit Prüfung des Modellprämissen
Die Korrelation von Arbeitserfahtung und Gehalt liegt bei 0.81 weshalb wir uns entscheiden haben diese als Regression zu verwenden. Des weiteren nutzen wir auch das Alter so wie das Bildungsniveau.
filtered_data3 %>%
ggplot() +
aes(x = Salary, y = Years.Of.Experience) +
geom_point(aes(color = Salary), alpha = 0.8) +
geom_smooth(method = lm, color = "orange") +
scale_color_viridis(option = "D")`geom_smooth()` using formula = 'y ~ x'
Rausnehmen aller nicht für die Regression relevanten Werte:
# Nur 'Salary' und 'Years.Of.Experience' behalten und den Rest entfernen
filtered_data5 <- filtered_data3 %>%
select(Salary, Years.Of.Experience)Zuerst führen wir dazu eine Z-Skalierung von filtered_data_5 durch.
Die Z-Skalierung ist eine Methode zur Standardisierung von numerischen Variablen, die in der linearen Regression und Entscheidungsbaum-Modellierung verwendet wird. Bei der Z-Skalierung werden alle numerischen Werte mit Ausnahme des Vorhersagewerts skaliert, um die Auswirkungen von Ausreißern zu minimieren und die Konvergenzgeschwindigkeit des Gradientenabstiegs zu erhöhen.
# Z-Skalierung der Variable "Years.Of.Experience"
filtered_data5_z <- filtered_data5
filtered_data5_z$Years.Of.Experience <- scale(filtered_data5$Years.Of.Experience)Ergebnis der Z-Skalierung:
summary(filtered_data5_z) Salary Years.Of.Experience.V1
Min. : 550 Min. :-1.270088
1st Qu.: 80000 1st Qu.:-0.771345
Median :122581 Median :-0.272603
Mean :122056 Mean : 0.000000
3rd Qu.:160000 3rd Qu.: 0.392387
Max. :240000 Max. : 4.216080
Überprüfung der Standardabweichung für Arbeitserfahrung
sd(filtered_data5_z$Years.Of.Experience)[1] 1
Aufteilung in Test- und Trainingsdaten:
set.seed(007)
filtered_data5_z <- initial_split(filtered_data5_z, prop = 0.8, strata = Years.Of.Experience)
fd5_train <- training(filtered_data5_z)
fd5_test <- testing(filtered_data5_z)Dieser Code teilt den Datensatz filtered_data5_z in Trainings- und Testdaten auf, um eine lineare Regression durchzuführen. Die Funktion set.seed(007) initialisiert den Zufallszahlengenerator mit einer festen Zahl, um sicherzustellen, dass die Ergebnisse bei jedem Durchlauf reproduzierbar sind. Die Funktion initial_split() aus dem Paket rsample teilt den Datensatz in Trainings- und Testdaten auf. Der Parameter prop = 0,8 gibt an, dass 80% der Daten für das Training verwendet werden sollen, während die restlichen 20% für das Testen verwendet werden. Der Parameter strata = Years.Of.Experience sorgt dafür, dass die Daten nach dem Gehalts-Wert stratifiziert werden, um sicherzustellen, dass die Trainings- und Testdaten eine ähnliche Verteilung von Gehalts-Werten aufweisen. Die Funktion training() extrahiert die Trainingsdaten aus dem aufgeteilten Datensatz, während testing() die Testdaten extrahiert.
Modell Initialisieren:
# Modell initialisieren
lm_model <- linear_reg() |> set_engine("lm")Lineare Regression:
# Lineare Regression von "Salary" basierend auf "Years.Of.Experience"
lm_fit <- lm_model |> fit(Salary ~ Years.Of.Experience, data = fd5_train)Zusammenfassung des Ergebnis:
# Zusammenfassung der Regression
summary <- lm_fit |> extract_fit_engine() |> summary()
summary
Call:
stats::lm(formula = Salary ~ Years.Of.Experience, data = data)
Residuals:
Min 1Q Median 3Q Max
-134867 -23921 -4848 18206 82152
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 121752.5 622.4 195.60 <2e-16 ***
Years.Of.Experience 36199.2 628.4 57.61 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 31300 on 2527 degrees of freedom
Multiple R-squared: 0.5677, Adjusted R-squared: 0.5675
F-statistic: 3319 on 1 and 2527 DF, p-value: < 2.2e-16
Hier alles erklären danke.
Vorhersagen auf Trainings- und Testdatensatz:
pred_train <- predict(lm_fit, new_data = fd5_train) |> rename("pred_train" = ".pred")
pred_test <- predict(lm_fit, new_data = fd5_test) |> rename("pred_test" = ".pred")
compare_train <- fd5_train |>
select(Salary) |>
bind_cols(pred_train)
head(compare_train)# A tibble: 6 × 2
Salary pred_train
<dbl> <dbl>
1 90000 93830.
2 70000 87812.
3 60000 81794.
4 90000 93830.
5 60000 81794.
6 85000 93830.
compare_test <- fd5_test |>
select(Salary) |>
bind_cols(pred_test)
head(compare_test)# A tibble: 6 × 2
Salary pred_test
<dbl> <dbl>
1 110000 105866.
2 75000 87812.
3 95000 99848.
4 90000 93830.
5 70000 87812.
6 95000 99848.
Grafische Darstellung:
# Daten für Training und Test
train_data <- cbind(fd5_train, pred_train)
test_data <- cbind(fd5_test, pred_test)
# Erstellen Sie eine ggplot-Grafik für die Trainingsdaten
ggplot(train_data, aes(x = Years.Of.Experience, y = Salary)) +
geom_point(color = viridis(0.50), alpha = 0.5) +
geom_line(aes(y = pred_train), color = "deeppink3", size = 1) +
labs(title = "Vorhersage auf Trainingsdaten",
x = "Years of Experience",
y = "Salary") +
scale_color_identity() + # Farben beibehalten
scale_y_continuous(labels = scales::comma) +
theme_minimal()Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
# Erstellen Sie eine ggplot-Grafik für die Testdaten
ggplot(test_data, aes(x = Years.Of.Experience, y = Salary)) +
geom_point(color = viridis(0.50), alpha = 0.5) +
geom_line(aes(y = pred_test), color = "deeppink3", size = 1) +
labs(title = "Vorhersage auf Testdaten",
x = "Years of Experience",
y = "Salary") +
scale_color_identity() + # Farben beibehalten
scale_y_continuous(labels = scales::comma) +
theme_minimal()Trainingsfehler:
rmse(compare_train, Salary, pred_train)# A tibble: 1 × 3
.metric .estimator .estimate
<chr> <chr> <dbl>
1 rmse standard 31290.
Testfehler:
rmse(compare_test, Salary, pred_test)# A tibble: 1 × 3
.metric .estimator .estimate
<chr> <chr> <dbl>
1 rmse standard 32154.
Verteilung von Arbeitserfahrung:
describe(filtered_data5, Salary)variable = Salary
type = double
na = 0 of 3 163 (0%)
unique = 334
min|max = 550 | 240 000
q05|q95 = 50 000 | 190 000
q25|q75 = 80 000 | 160 000
median = 122 581
mean = 122 055.8
Residuen (auch als Fehler oder Residuals bezeichnet) sind die Differenzen zwischen den beobachteten Werten und den vorhergesagten Werten in einem Regressionsmodell. Sie repräsentieren die Abweichungen zwischen den tatsächlichen Daten und den Werten, die das Modell vorhersagt. Idealerweise sollten die Residuen normalverteilt sein, um sicherzustellen, dass das Regressionsmodell angemessen ist.
Es ist üblich, die Residuen sowohl für Trainingsdaten als auch für Testdaten zu überprüfen, um die Leistung des Modells auf beiden Datensätzen zu evaluieren. Hier sind einige Gründe, warum es wichtig ist, die Residuen auf beiden Datensätzen zu betrachten:
Trainingsdaten:
Testdaten:
In Ihrer speziellen Situation könnten Sie die Residuen für beide Datensätze betrachten, indem Sie die augment()-Funktion für Trainings- und Testdaten separat aufrufen, wie im vorherigen Beispiel gezeigt.
# Residuen mit augment() auf den Trainingsdaten abrufen
residuals_train <- augment(lm_fit, new_data = fd5_train) %>% select(.resid)
# Residuen mit augment() auf den Testdaten abrufen
residuals_test <- augment(lm_fit, new_data = fd5_test) %>% select(.resid)
# Ausgabe der Residuen für Trainingsdaten
print(residuals_train)# A tibble: 2,529 × 1
.resid
<dbl>
1 -3830.
2 -17812.
3 -21794.
4 -3830.
5 -21794.
6 -8830.
7 -26794.
8 -21794.
9 -26794.
10 -17812.
# ℹ 2,519 more rows
# Ausgabe der Residuen für Testdaten
print(residuals_test)# A tibble: 634 × 1
.resid
<dbl>
1 4134.
2 -12812.
3 -4848.
4 -3830.
5 -17812.
6 -4848.
7 -21794.
8 -17812.
9 -17812.
10 8115.
# ℹ 624 more rows
Histogramm der Residuen
# Standardisierung/Z-Skalierung der Residuen der Residuen
residuals_train$standardized_resid <- scale(residuals_train$.resid)
# Histogramm der standardisierten Residuen mit Viridis-Farbschema
ggplot(data = residuals_train, aes(x = standardized_resid)) +
geom_histogram(binwidth = 0.5, fill = viridis(1), color = "black", alpha = 0.7) +
labs(title = "Histogramm der standardisierten Residuen",
x = "Standardisierte Residuen",
y = "Häufigkeit") +
scale_fill_viridis() + # Fügt das Viridis-Farbschema hinzu
theme_minimal()# Standardisierung/Z-Skalierung der Residuen der Residuen
residuals_test$standardized_resid <- scale(residuals_test$.resid)
# Histogramm der standardisierten Residuen mit Viridis-Farbschema
ggplot(data = residuals_test, aes(x = standardized_resid)) +
geom_histogram(binwidth = 0.5, fill = viridis(1), color = "black", alpha = 0.7) +
labs(title = "Histogramm der standardisierten Residuen",
x = "Standardisierte Residuen",
y = "Häufigkeit") +
scale_fill_viridis() + # Fügt das Viridis-Farbschema hinzu
theme_minimal()(Quantil-Quantil-Diagramm): überprüft die Residuen auf Normalverteilung. Die Quantile der standardisierten Residuen werden gegen die Quantile der Normalverteilung geplottet. Unter der Normalitätsannahme sollten die Punkte zufällig entlang der Diagonale (x=y) streuen (Umschreiben)
Trainingsdaten
# QQ-Plot erstellen
ggplot(data = residuals_train, aes(sample = standardized_resid)) +
stat_qq(distribution = qnorm, dparams = list(mean = 0, sd = 1), color = viridis(1)) +
geom_abline(intercept = 0, slope = 1, linetype = "dashed", color = "black") +
labs(title = "QQ-Plot der standardisierten Residuen",
x = "Quantile der Normalverteilung",
y = "Quantile der Residuen") +
scale_color_viridis() + # Fügt das Viridis-Farbschema hinzu
theme_minimal()Testdaten
residuals_test$standardized_resid <- scale(residuals_test$.resid)
# QQ-Plot erstellen
ggplot(data = residuals_test, aes(sample = standardized_resid)) +
stat_qq(distribution = qnorm, dparams = list(mean = 0, sd = 1), color = viridis(1)) +
geom_abline(intercept = 0, slope = 1, linetype = "dashed", color = "black") +
labs(title = "QQ-Plot der standardisierten Residuen",
x = "Quantile der Normalverteilung",
y = "Quantile der Residuen") +
scale_color_viridis() + # Fügt das Viridis-Farbschema hinzu
theme_minimal()Bildet die ausreißer vom Gehalt nach oben und unten ab welche wir im ersten Diagramm von 7.1 sehen, die Residuen sind nicht perfekt normalverteilt. Eventuell gibt es hier Datenpunkte welche die Residuen beeinflussen. Aufgrund der explorativen Datenanalyse wissen wir das Jobs mit hoher Arbeitserfahrung oft Führungsverantwortung beinhalten welche nochmal zusätzlich monetär honoriert wird. Die Flache Linie durch die Gerade durch deutet auf ein Gehaltscap hin. Die Ausreißer am unteren Ende lassen sich durch Einstiegsjobs so wie Niedriglohnjobs ohne Hochschulabchlüsse erklären. (Ausbildungsberufe werden hier nicht berücksichtigt).
Hätte man alle Betrachtungen under der gleichstellung von Years of Experience und Education level betrachten sollen bei länder und geschlechter vergleichen?